1use crate::ext::io::*;
3use crate::scripts::base::*;
4use crate::types::*;
5use crate::utils::encoding::*;
6use crate::utils::serde_base64bytes::*;
7use crate::utils::struct_pack::*;
8use anyhow::Result;
9use serde::{Deserialize, Serialize};
10use std::any::Any;
11use std::io::{Read, Seek, Write};
12
13#[derive(Debug, Serialize, Deserialize)]
14struct YSVRData {
15 version: u32,
16 variables: Vec<Variable>,
17}
18
19impl StructUnpack for YSVRData {
20 fn unpack<R: Read + Seek>(
21 reader: &mut R,
22 big: bool,
23 encoding: Encoding,
24 info: &Option<Box<dyn Any>>,
25 ) -> Result<Self> {
26 let version = u32::unpack(reader, big, encoding, info)?;
27 let ninfo = Box::new(version) as Box<dyn Any>;
28 let count = u16::unpack(reader, big, encoding, info)?;
29 let variables = reader.read_struct_vec(count as usize, big, encoding, &Some(ninfo))?;
30 Ok(Self { version, variables })
31 }
32}
33
34impl StructPack for YSVRData {
35 fn pack<W: Write>(
36 &self,
37 writer: &mut W,
38 big: bool,
39 encoding: Encoding,
40 info: &Option<Box<dyn Any>>,
41 ) -> Result<()> {
42 self.version.pack(writer, big, encoding, info)?;
43 let ninfo = Box::new(self.version) as Box<dyn Any>;
44 let count = self.variables.len() as u16;
45 count.pack(writer, big, encoding, info)?;
46 let info = &Some(ninfo);
47 for variable in &self.variables {
48 variable.pack(writer, big, encoding, info)?;
49 }
50 Ok(())
51 }
52}
53
54fn get_info_as_version(info: &Option<Box<dyn Any>>) -> Result<u32> {
55 Ok(*info
56 .as_ref()
57 .ok_or_else(|| anyhow::anyhow!("info not found"))?
58 .downcast_ref()
59 .ok_or_else(|| anyhow::anyhow!("not YSVR version"))?)
60}
61
62#[derive(Debug, Clone, Serialize, Deserialize)]
63#[serde(untagged)]
64enum VariableValue {
65 None,
66 Int(i64),
67 Double(f64),
68 ByteString { raw: Base64Bytes },
69 MString(String),
70}
71
72impl StructUnpack for VariableValue {
73 fn unpack<R: Read + Seek>(
74 reader: &mut R,
75 big: bool,
76 encoding: Encoding,
77 info: &Option<Box<dyn Any>>,
78 ) -> Result<Self> {
79 let var_type = *info
80 .as_ref()
81 .and_then(|i| i.downcast_ref::<u8>())
82 .ok_or_else(|| anyhow::anyhow!("VariableValue: type info missing"))?;
83 match var_type {
84 0 => Ok(Self::None),
85 1 => {
86 let v = i64::unpack(reader, big, encoding, info)?;
87 Ok(Self::Int(v))
88 }
89 2 => {
90 let v = f64::unpack(reader, big, encoding, info)?;
91 Ok(Self::Double(v))
92 }
93 3 => {
94 let len = u16::unpack(reader, big, encoding, info)? as usize;
95 let mut buf = vec![0u8; len];
96 reader.read_exact(&mut buf)?;
97 if buf.starts_with(b"M") && buf.len() >= 3 {
98 let len = u16::from_le_bytes([buf[1], buf[2]]);
99 if buf.len() >= len as usize + 3 {
100 if let Ok(s) = decode_to_string(encoding, &buf[3..], true) {
101 return Ok(Self::MString(s));
102 }
103 }
104 }
105 Ok(Self::ByteString { raw: buf.into() })
106 }
107 _ => anyhow::bail!(
108 "Unknown variable type: {} at {}",
109 var_type,
110 reader.stream_position()?
111 ),
112 }
113 }
114}
115
116impl StructPack for VariableValue {
117 fn pack<W: Write>(
118 &self,
119 writer: &mut W,
120 big: bool,
121 encoding: Encoding,
122 info: &Option<Box<dyn Any>>,
123 ) -> Result<()> {
124 match self {
125 Self::Int(v) => v.pack(writer, big, encoding, info)?,
126 Self::Double(v) => v.pack(writer, big, encoding, info)?,
127 Self::ByteString { raw } => {
128 let len = raw.len() as u16;
129 len.pack(writer, big, encoding, info)?;
130 writer.write_all(&raw)?;
131 }
132 Self::MString(s) => {
133 let encoded = encode_string(encoding, &s, true)?;
134 let len = encoded.len() as u16;
135 (len + 3).pack(writer, big, encoding, info)?;
136 writer.write_u8(b'M')?;
137 writer.write_u16(len)?;
138 writer.write_all(&encoded)?;
139 }
140 Self::None => {}
141 }
142 Ok(())
143 }
144}
145
146#[derive(Debug, Clone, Serialize, Deserialize)]
147struct Variable {
148 scope: u8,
149 #[serde(skip_serializing_if = "Option::is_none")]
151 unk: Option<u8>,
152 script_id: u16,
153 variable_index: u16,
154 dimension_sizes: Vec<u32>,
155 value: VariableValue,
156}
157
158impl StructUnpack for Variable {
159 fn unpack<R: Read + Seek>(
160 reader: &mut R,
161 big: bool,
162 encoding: Encoding,
163 info: &Option<Box<dyn Any>>,
164 ) -> Result<Self> {
165 let version = get_info_as_version(info)?;
166
167 let scope = reader.read_u8()?;
168 let unk = if version >= 494 {
169 Some(reader.read_u8()?)
170 } else {
171 None
172 };
173 let script_id = reader.read_u16()?;
174 let variable_index = reader.read_u16()?;
175 let variable_type = reader.read_u8()?;
176 let num_dimensions = reader.read_u8()?;
177 let mut dimension_sizes = Vec::with_capacity(num_dimensions as usize);
178 for _ in 0..num_dimensions {
179 dimension_sizes.push(reader.read_u32()?);
180 }
181 let value_info = Box::new(variable_type) as Box<dyn Any>;
182 let value = VariableValue::unpack(reader, big, encoding, &Some(value_info))?;
183
184 Ok(Self {
185 scope,
186 unk,
187 script_id,
188 variable_index,
189 dimension_sizes,
190 value,
191 })
192 }
193}
194
195impl StructPack for Variable {
196 fn pack<W: Write>(
197 &self,
198 writer: &mut W,
199 big: bool,
200 encoding: Encoding,
201 info: &Option<Box<dyn Any>>,
202 ) -> Result<()> {
203 let version = get_info_as_version(info)?;
204
205 writer.write_u8(self.scope)?;
206 if version >= 494 {
207 writer.write_u8(self.unk.unwrap_or(0))?;
208 }
209 writer.write_u16(self.script_id)?;
210 writer.write_u16(self.variable_index)?;
211 writer.write_u8(match &self.value {
212 VariableValue::None => 0,
213 VariableValue::Int(_) => 1,
214 VariableValue::Double(_) => 2,
215 VariableValue::ByteString { .. } => 3,
216 VariableValue::MString(_) => 3,
217 })?;
218 writer.write_u8(self.dimension_sizes.len() as u8)?;
219 for &size in &self.dimension_sizes {
220 writer.write_u32(size)?;
221 }
222 self.value.pack(writer, big, encoding, &None)?;
223 Ok(())
224 }
225}
226
227#[derive(Debug)]
228pub struct YSVRBuilder {}
229
230impl YSVRBuilder {
231 pub const fn new() -> Self {
233 YSVRBuilder {}
234 }
235}
236
237impl ScriptBuilder for YSVRBuilder {
238 fn default_encoding(&self) -> Encoding {
239 Encoding::Cp932
240 }
241
242 fn build_script(
243 &self,
244 buf: Vec<u8>,
245 _filename: &str,
246 encoding: Encoding,
247 _archive_encoding: Encoding,
248 config: &ExtraConfig,
249 _archive: Option<&Box<dyn Script>>,
250 ) -> Result<Box<dyn Script + Send + Sync>> {
251 Ok(Box::new(YSVR::new(MemReader::new(buf), encoding, config)?))
252 }
253
254 fn extensions(&self) -> &'static [&'static str] {
255 &["ybn"]
256 }
257
258 fn is_this_format(&self, _filename: &str, buf: &[u8], buf_len: usize) -> Option<u8> {
259 if buf_len >= 4 && buf.starts_with(b"YSVR") {
260 return Some(20);
261 }
262 None
263 }
264
265 fn script_type(&self) -> &'static ScriptType {
266 &ScriptType::YurisYSVR
267 }
268
269 fn can_create_file(&self) -> bool {
270 true
271 }
272
273 fn create_file<'a>(
274 &'a self,
275 filename: &'a str,
276 writer: Box<dyn WriteSeek + 'a>,
277 encoding: Encoding,
278 file_encoding: Encoding,
279 config: &ExtraConfig,
280 ) -> Result<()> {
281 create_file(
282 filename,
283 writer,
284 encoding,
285 file_encoding,
286 config.custom_yaml,
287 )
288 }
289}
290
291#[derive(Debug)]
292pub struct YSVR {
293 data: YSVRData,
294 custom_yaml: bool,
295}
296
297impl YSVR {
298 pub fn new<T: Read + Seek>(
299 mut reader: T,
300 encoding: Encoding,
301 config: &ExtraConfig,
302 ) -> Result<Self> {
303 let mut sig = [0; 4];
304 reader.read_exact(&mut sig)?;
305 if &sig != b"YSVR" {
306 anyhow::bail!("Unsupported YSVR file.");
307 }
308 let data = YSVRData::unpack(&mut reader, false, encoding, &None)?;
309 Ok(Self {
310 data,
311 custom_yaml: config.custom_yaml,
312 })
313 }
314}
315
316impl Script for YSVR {
317 fn default_output_script_type(&self) -> OutputScriptType {
318 OutputScriptType::Custom
319 }
320
321 fn is_output_supported(&self, output: OutputScriptType) -> bool {
322 matches!(output, OutputScriptType::Custom)
323 }
324
325 fn default_format_type(&self) -> FormatOptions {
326 FormatOptions::None
327 }
328
329 fn custom_output_extension(&self) -> &'static str {
330 if self.custom_yaml { "yaml" } else { "json" }
331 }
332
333 fn custom_export(&self, filename: &std::path::Path, encoding: Encoding) -> Result<()> {
334 let s = if self.custom_yaml {
335 serde_yaml_ng::to_string(&self.data)
336 .map_err(|e| anyhow::anyhow!("Failed to serialize to YAML: {}", e))?
337 } else {
338 serde_json::to_string_pretty(&self.data)
339 .map_err(|e| anyhow::anyhow!("Failed to serialize to JSON: {}", e))?
340 };
341 let mut writer = crate::utils::files::write_file(filename)?;
342 let s = encode_string(encoding, &s, false)?;
343 writer.write_all(&s)?;
344 writer.flush()?;
345 Ok(())
346 }
347
348 fn custom_import<'a>(
349 &'a self,
350 custom_filename: &'a str,
351 file: Box<dyn WriteSeek + 'a>,
352 encoding: Encoding,
353 output_encoding: Encoding,
354 ) -> Result<()> {
355 create_file(
356 custom_filename,
357 file,
358 encoding,
359 output_encoding,
360 self.custom_yaml,
361 )
362 }
363}
364
365fn create_file<'a>(
366 custom_filename: &'a str,
367 mut writer: Box<dyn WriteSeek + 'a>,
368 encoding: Encoding,
369 output_encoding: Encoding,
370 yaml: bool,
371) -> Result<()> {
372 let input = crate::utils::files::read_file(custom_filename)?;
373 let s = decode_to_string(output_encoding, &input, true)?;
374 let data: YSVRData = if yaml {
375 serde_yaml_ng::from_str(&s).map_err(|e| anyhow::anyhow!("Failed to parse YAML: {}", e))?
376 } else {
377 serde_json::from_str(&s).map_err(|e| anyhow::anyhow!("Failed to parse JSON: {}", e))?
378 };
379 writer.write_all(b"YSVR")?;
380 data.pack(&mut writer, false, encoding, &None)?;
381 Ok(())
382}